package com.xzzz.expand.gateway.dr;

import cn.hutool.core.util.StrUtil;
import com.xzzz.common.base.exception.XzException400;
import com.xzzz.common.base.pojo.R;
import com.xzzz.common.base.pojo.RCode;
import com.xzzz.common.base.util.SortUtil;
import com.xzzz.expand.gateway.dr.pojo.GwR;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.actuate.AbstractGatewayControllerEndpoint;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory;
import org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.core.Ordered;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 动态路由
 *
 * @author xzzz
 */
@Slf4j
@RestController
@RequestMapping("/gateway")
public class GatewayController extends AbstractGatewayControllerEndpoint {

    public GatewayController(RouteDefinitionLocator routeDefinitionLocator,
                             List<GlobalFilter> globalFilters,
                             List<GatewayFilterFactory> gatewayFilters,
                             List<RoutePredicateFactory> routePredicates,
                             RouteDefinitionWriter routeDefinitionWriter,
                             RouteLocator routeLocator) {
        super(routeDefinitionLocator, globalFilters, gatewayFilters, routePredicates, routeDefinitionWriter, routeLocator);
    }

    @Autowired
    private RouteUtil routeUtil;

    /**
     * 路由列表
     * @return 路由列表
     */
    @GetMapping("/dr/routes")
    public Mono<GwR> routes() {
        List<RouteDefinition> list = new ArrayList<>();
        this.routeDefinitionLocator.getRouteDefinitions()
                // 转为同步方法
                .toStream()
                // 放入集合
                .forEach(list::add);
        return Mono.just(GwR.ok(list));
    }

    /**
     * 路由名称列表列表
     * @return 路由列表
     */
    @GetMapping("/dr/routeids")
    public Mono<GwR> routeIds() {
        List<String> list = new ArrayList<>();
        this.routeDefinitionLocator.getRouteDefinitions()
                // 转为同步方法
                .toStream()
                // 只取id
                .map(RouteDefinition::getId)
                // id 进行排序
                .sorted(SortUtil.strSort)
                .forEach(list::add);
        return Mono.just(GwR.ok(list));
    }

    /**
     * 路由详情
     * @param routeId 路由ID
     * @return 路由详情
     */
    @GetMapping("/dr/route")
    public Mono<GwR> route(@RequestParam("routeId") String routeId) {
        if (StrUtil.isBlank(routeId)) {
            throw new XzException400("路由ID不得为空");
        }
        return this.routeDefinitionLocator.getRouteDefinitions()
                // 从路由列表中保留查询的路由ID
                .filter(route -> route.getId().equals(routeId))
                // 如果有一条
                .singleOrEmpty()
                // 有数据则进入map
                .map(GwR::ok)
                // 如果没有数据, 则进入该方法
                .switchIfEmpty(
                    Mono.just(
                        GwR.fault(RCode.NOT_FOUND.getCode(), String.format("查询路由失败, 路由[%s]不存在", routeId))
                    )
                );
    }

    /**
     * 路由绑定的过滤器
     * @param routeId 路由ID
     * @return 过滤器列表
     */
    @GetMapping("/dr/combinedfilters")
    public Mono<GwR> routeCombinedFilters(@RequestParam("routeId") String routeId) {
        // TODO: missing global filters
        return this.routeLocator.getRoutes()
                .filter(route -> route.getId().equals(routeId))
                .singleOrEmpty()
                .map(route -> GwR.ok(route.getFilters().stream().map(gatewayFilter -> gatewayFilter.getClass().getName())))
                .switchIfEmpty(
                    Mono.just(
                        GwR.fault(RCode.NOT_FOUND.getCode(), String.format("查询路由失败, 路由[%s]不存在", routeId))
                    )
                );

    }

    /**
     * 保存路由, 新增或修改
     * @param route 路由内容
     */
    @PostMapping("/dr/route")
    public Mono<GwR> saveRoute(@RequestBody RouteDefinition route) {
        return Mono.just(route)
                .doOnNext(routeDefinition -> routeUtil.validateRouteDefinition(routeDefinition))
                .flatMap(routeDefinition -> this.routeDefinitionWriter.save(Mono.just(routeDefinition).map(r -> {
                    r.setId(route.getId());
                    log.debug("保存路由 {}: {}", route.getId() ,route.getUri().toString());
                    return r;
                }))
                .then(Mono.defer(() -> Mono.just(GwR.ok(String.format("路由[%s]保存成功", route.getId()))))))
                .switchIfEmpty(
                    Mono.defer(
                        () -> Mono.just(GwR.fault(""))
                    )
                );
    }


    /**
     * 删除路由
     * @param routeId 路由ID, 必填
     */
    @DeleteMapping("/dr/route")
    public Mono<GwR> deleteRoute(@RequestParam("routeId") String routeId) {
        return this.routeDefinitionWriter.delete(Mono.just(routeId))
                // 创建一个新的Mono, Mono.defer 为立即创建
                .then(Mono.defer(() -> Mono.just(GwR.ok())))
                // 如果delete时发生错误, 使用fallback来创建新的Mono
                .onErrorResume(
                    ex -> ex instanceof NotFoundException,
                    fallback -> Mono.just(
                    GwR.fault(RCode.NOT_FOUND.getCode(), String.format("删除路由失败, 路由[%s]不存在", routeId))
                    )
                );
    }

    /**
     * 刷新路由
     */
    @GetMapping("/dr/refresh")
    public R<?> drRefresh() {
        log.info("刷新路由");
        super.refresh();
        return R.ok();
    }

    /**
     * 查询全局过滤器
     * @return 过滤器列表
     */
    @GetMapping("/dr/globalfilters")
    public R<List<HashMap<String,Object>>> drGlobalFilters() {
        return R.ok(super.globalFilters.stream().map(filter -> {
            HashMap<String,Object> map = new HashMap<>();
            map.put("filterName", filter.toString());
            map.put("order", ((Ordered) filter).getOrder());
            return map;
        }).sorted((f2, f1) -> {
            int o1 = Integer.parseInt(f1.get("order").toString());
            int o2 = Integer.parseInt(f2.get("order").toString());
            return Integer.compare(o2, o1);
        }).collect(Collectors.toList()));
    }

    /**
     * 查询可选过滤器
     * @return 过滤器列表
     */
    @GetMapping("/dr/routefilters")
    public R<List<HashMap<String,Object>>> drRouteFilters() {
        return R.ok(super.GatewayFilters.stream().map(filter -> {
            HashMap<String,Object> map = new HashMap<>();
            map.put("filterName", filter.toString());
            if (filter instanceof Ordered) {
                map.put("order", ((Ordered) filter).getOrder());
            } else {
                map.put("order", 0);
            }
            return map;
        }).sorted((f2, f1) -> {
            int o1 = Integer.parseInt(f1.get("order").toString());
            int o2 = Integer.parseInt(f2.get("order").toString());
            return Integer.compare(o2, o1);
        }).collect(Collectors.toList()));
    }

}
